#!/bin/bash

# Flutter包名修改脚本
# 功能：一键修改Flutter项目的包名（Bundle ID）
# 支持：Android和iOS平台

set -e  # 遇到错误立即退出

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 脚本信息
SCRIPT_NAME="Flutter包名修改工具"
VERSION="1.0.0"

# 打印带颜色的信息
print_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

print_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 显示帮助信息
show_help() {
    cat << EOF
${SCRIPT_NAME} v${VERSION}

用法: $0 [选项] <新包名>

说明:
  一键修改Flutter项目的包名（Bundle ID），支持Android和iOS平台。
  会自动备份原始文件，确保安全。

参数:
  新包名    新的包名，格式如: com.company.appname

选项:
  -n, --name NAME      应用名称 (可选，默认使用项目名称)
  -b, --backup         创建备份文件 (默认启用)
  --no-backup          不创建备份文件
  -f, --force          强制执行，不询问确认
  -v, --verbose        详细输出
  -h, --help           显示此帮助信息

示例:
  $0 com.mycompany.myapp
  $0 com.mycompany.myapp -n "My App"
  $0 com.mycompany.myapp --force --verbose

注意:
  - 包名格式必须符合: com.company.appname
  - 执行前请确保项目代码已提交到Git
  - 建议在执行后运行 flutter clean && flutter pub get

EOF
}

# 验证包名格式
validate_package_name() {
    local package_name="$1"
    
    # 检查基本格式
    if [[ ! "$package_name" =~ ^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$ ]]; then
        print_error "包名格式无效: $package_name"
        print_error "正确格式示例: com.company.appname"
        print_error "要求:"
        print_error "  - 只能包含小写字母、数字和下划线"
        print_error "  - 必须包含至少两个部分，用点分隔"
        print_error "  - 每个部分必须以字母开头"
        return 1
    fi
    
    # 检查是否包含保留字
    local reserved_words=("android" "java" "javax" "com.android" "com.google")
    for word in "${reserved_words[@]}"; do
        if [[ "$package_name" == *"$word"* ]]; then
            print_warning "包名包含保留字: $word"
            print_warning "这可能会导致问题，建议使用其他包名"
        fi
    done
    
    return 0
}

# 检查项目环境
check_environment() {
    print_info "检查项目环境..."
    
    # 检查是否在Flutter项目根目录
    if [[ ! -f "pubspec.yaml" ]]; then
        print_error "未找到 pubspec.yaml 文件"
        print_error "请在Flutter项目根目录下运行此脚本"
        exit 1
    fi
    
    # 检查Android目录
    if [[ ! -d "android" ]]; then
        print_error "未找到 android 目录"
        exit 1
    fi
    
    # 检查iOS目录
    if [[ ! -d "ios" ]]; then
        print_warning "未找到 ios 目录，将跳过iOS配置"
        HAS_IOS=false
    else
        HAS_IOS=true
    fi
    
    # 检查Git状态
    if command -v git >/dev/null 2>&1 && [[ -d ".git" ]]; then
        if [[ -n "$(git status --porcelain)" ]]; then
            print_warning "Git工作区有未提交的更改"
            print_warning "建议先提交更改再修改包名"
            if [[ "$FORCE" != "true" ]]; then
                read -p "是否继续? (y/N): " -n 1 -r
                echo
                if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                    exit 1
                fi
            fi
        fi
    fi
}

# 获取当前包名
get_current_package_name() {
    # 从Android配置获取当前包名
    if [[ -f "android/app/build.gradle" ]]; then
        CURRENT_PACKAGE=$(grep -E "^\s*applicationId\s*=" android/app/build.gradle | sed -E 's/.*applicationId\s*=\s*"([^"]+)".*/\1/')
        if [[ -n "$CURRENT_PACKAGE" ]]; then
            print_info "当前包名: $CURRENT_PACKAGE"
            return 0
        fi
    fi
    
    # 从Android Manifest获取
    if [[ -f "android/app/src/main/AndroidManifest.xml" ]]; then
        CURRENT_PACKAGE=$(grep -E 'package\s*=' android/app/src/main/AndroidManifest.xml | sed -E 's/.*package="([^"]+)".*/\1/')
        if [[ -n "$CURRENT_PACKAGE" ]]; then
            print_info "当前包名: $CURRENT_PACKAGE"
            return 0
        fi
    fi
    
    print_warning "无法自动检测当前包名"
    CURRENT_PACKAGE=""
}

# 获取应用名称
get_app_name() {
    if [[ -n "$APP_NAME" ]]; then
        return 0
    fi
    
    # 从pubspec.yaml获取项目名称
    if [[ -f "pubspec.yaml" ]]; then
        APP_NAME=$(grep -E "^name:\s*" pubspec.yaml | sed -E 's/name:\s*(.+)/\1/' | tr -d '"'"'"'')
        if [[ -n "$APP_NAME" ]]; then
            print_info "应用名称: $APP_NAME"
            return 0
        fi
    fi
    
    # 使用目录名作为默认应用名称
    APP_NAME=$(basename "$(pwd)")
    print_info "使用目录名作为应用名称: $APP_NAME"
}

# 创建备份
create_backup() {
    if [[ "$CREATE_BACKUP" != "true" ]]; then
        return 0
    fi
    
    local backup_dir="backup_$(date +%Y%m%d_%H%M%S)"
    print_info "创建备份: $backup_dir"
    
    mkdir -p "$backup_dir"
    
    # 备份关键文件
    local files_to_backup=(
        "android/app/build.gradle"
        "android/app/src/main/AndroidManifest.xml"
        "android/app/src/debug/AndroidManifest.xml"
        "android/app/src/profile/AndroidManifest.xml"
    )
    
    if [[ "$HAS_IOS" == "true" ]]; then
        files_to_backup+=(
            "ios/Runner/Info.plist"
            "ios/Runner.xcodeproj/project.pbxproj"
        )
    fi
    
    for file in "${files_to_backup[@]}"; do
        if [[ -f "$file" ]]; then
            local backup_file="$backup_dir/$file"
            mkdir -p "$(dirname "$backup_file")"
            cp "$file" "$backup_file"
            if [[ "$VERBOSE" == "true" ]]; then
                print_info "备份: $file -> $backup_file"
            fi
        fi
    done
    
    print_success "备份完成: $backup_dir"
    BACKUP_DIR="$backup_dir"
}

# 更新Android配置
update_android_config() {
    print_info "更新Android配置..."
    
    # 更新build.gradle
    if [[ -f "android/app/build.gradle" ]]; then
        print_info "更新 android/app/build.gradle"
        
        # 更新namespace
        sed -i.tmp "s/namespace = \".*\"/namespace = \"$NEW_PACKAGE\"/" android/app/build.gradle
        
        # 更新applicationId
        sed -i.tmp "s/applicationId = \".*\"/applicationId = \"$NEW_PACKAGE\"/" android/app/build.gradle
        
        # 删除临时文件
        rm -f android/app/build.gradle.tmp
        
        print_success "已更新 build.gradle"
    fi
    
    # 更新AndroidManifest.xml文件
    local manifest_files=(
        "android/app/src/main/AndroidManifest.xml"
        "android/app/src/debug/AndroidManifest.xml"
        "android/app/src/profile/AndroidManifest.xml"
    )
    
    for manifest in "${manifest_files[@]}"; do
        if [[ -f "$manifest" ]]; then
            print_info "更新 $manifest"
            
            # 更新package属性
            sed -i.tmp "s/package=\"[^\"]*\"/package=\"$NEW_PACKAGE\"/" "$manifest"
            
            # 删除临时文件
            rm -f "$manifest.tmp"
            
            print_success "已更新 $manifest"
        fi
    done
    
    # 更新MainActivity路径（如果存在）
    update_main_activity_path
}

# 更新MainActivity路径
update_main_activity_path() {
    local old_path_array=(${CURRENT_PACKAGE//./ })
    local new_path_array=(${NEW_PACKAGE//./ })
    
    local old_path=$(IFS=/; echo "${old_path_array[*]}")
    local new_path=$(IFS=/; echo "${new_path_array[*]}")
    
    local old_activity_dir="android/app/src/main/kotlin/$old_path"
    local new_activity_dir="android/app/src/main/kotlin/$new_path"
    
    if [[ -d "$old_activity_dir" ]]; then
        print_info "移动MainActivity文件..."
        
        # 创建新目录结构
        mkdir -p "$new_activity_dir"
        
        # 移动文件
        if [[ -f "$old_activity_dir/MainActivity.kt" ]]; then
            # 更新包名声明
            sed "s/package $CURRENT_PACKAGE/package $NEW_PACKAGE/" "$old_activity_dir/MainActivity.kt" > "$new_activity_dir/MainActivity.kt"
            rm "$old_activity_dir/MainActivity.kt"
            print_success "已移动并更新 MainActivity.kt"
        fi
        
        # 清理旧目录
        find "android/app/src/main/kotlin" -type d -empty -delete 2>/dev/null || true
    fi
    
    # 检查Java目录
    old_activity_dir="android/app/src/main/java/$old_path"
    new_activity_dir="android/app/src/main/java/$new_path"
    
    if [[ -d "$old_activity_dir" ]]; then
        print_info "移动Java MainActivity文件..."
        
        # 创建新目录结构
        mkdir -p "$new_activity_dir"
        
        # 移动文件
        if [[ -f "$old_activity_dir/MainActivity.java" ]]; then
            # 更新包名声明
            sed "s/package $CURRENT_PACKAGE;/package $NEW_PACKAGE;/" "$old_activity_dir/MainActivity.java" > "$new_activity_dir/MainActivity.java"
            rm "$old_activity_dir/MainActivity.java"
            print_success "已移动并更新 MainActivity.java"
        fi
        
        # 清理旧目录
        find "android/app/src/main/java" -type d -empty -delete 2>/dev/null || true
    fi
}

# 更新iOS配置
update_ios_config() {
    if [[ "$HAS_IOS" != "true" ]]; then
        return 0
    fi
    
    print_info "更新iOS配置..."
    
    # 更新Info.plist
    if [[ -f "ios/Runner/Info.plist" ]]; then
        print_info "更新 ios/Runner/Info.plist"
        
        # 使用PlistBuddy更新Bundle Identifier
        if command -v /usr/libexec/PlistBuddy >/dev/null 2>&1; then
            /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $NEW_PACKAGE" ios/Runner/Info.plist
            print_success "已更新 Info.plist (使用PlistBuddy)"
        else
            # 使用sed作为备选方案
            sed -i.tmp "s/<string>.*<\/string>/<string>$NEW_PACKAGE<\/string>/" ios/Runner/Info.plist
            rm -f ios/Runner/Info.plist.tmp
            print_success "已更新 Info.plist (使用sed)"
        fi
    fi
    
    # 更新project.pbxproj
    if [[ -f "ios/Runner.xcodeproj/project.pbxproj" ]]; then
        print_info "更新 ios/Runner.xcodeproj/project.pbxproj"
        
        # 更新PRODUCT_BUNDLE_IDENTIFIER
        sed -i.tmp "s/PRODUCT_BUNDLE_IDENTIFIER = [^;]*/PRODUCT_BUNDLE_IDENTIFIER = $NEW_PACKAGE/" ios/Runner.xcodeproj/project.pbxproj
        
        # 删除临时文件
        rm -f ios/Runner.xcodeproj/project.pbxproj.tmp
        
        print_success "已更新 project.pbxproj"
    fi
}

# 清理和重新构建
cleanup_and_rebuild() {
    print_info "清理项目..."
    
    # 使用fvm flutter（根据CLAUDE.md指示）
    if command -v fvm >/dev/null 2>&1; then
        FLUTTER_CMD="fvm flutter"
    else
        FLUTTER_CMD="flutter"
    fi
    
    # 清理项目
    $FLUTTER_CMD clean >/dev/null 2>&1 || true
    
    # 获取依赖
    print_info "获取依赖..."
    $FLUTTER_CMD pub get >/dev/null 2>&1 || true
    
    print_success "项目清理完成"
}

# 验证修改结果
verify_changes() {
    print_info "验证修改结果..."
    
    local issues_found=false
    
    # 检查Android配置
    if [[ -f "android/app/build.gradle" ]]; then
        if grep -q "applicationId = \"$NEW_PACKAGE\"" android/app/build.gradle; then
            print_success "✓ Android applicationId 已更新"
        else
            print_error "✗ Android applicationId 更新失败"
            issues_found=true
        fi
        
        if grep -q "namespace = \"$NEW_PACKAGE\"" android/app/build.gradle; then
            print_success "✓ Android namespace 已更新"
        else
            print_error "✗ Android namespace 更新失败"
            issues_found=true
        fi
    fi
    
    # 检查iOS配置
    if [[ "$HAS_IOS" == "true" && -f "ios/Runner/Info.plist" ]]; then
        if grep -q "$NEW_PACKAGE" ios/Runner/Info.plist; then
            print_success "✓ iOS Bundle Identifier 已更新"
        else
            print_error "✗ iOS Bundle Identifier 更新失败"
            issues_found=true
        fi
    fi
    
    if [[ "$issues_found" == "true" ]]; then
        print_error "发现问题，请检查上述错误"
        if [[ -n "$BACKUP_DIR" ]]; then
            print_info "可以从备份恢复: $BACKUP_DIR"
        fi
        return 1
    else
        print_success "所有配置文件已成功更新！"
        return 0
    fi
}

# 显示完成信息
show_completion_info() {
    echo
    print_success "✅ 包名修改完成！"
    echo
    print_info "修改摘要:"
    print_info "  旧包名: ${CURRENT_PACKAGE:-"未知"}"
    print_info "  新包名: $NEW_PACKAGE"
    print_info "  应用名称: $APP_NAME"
    if [[ -n "$BACKUP_DIR" ]]; then
        print_info "  备份位置: $BACKUP_DIR"
    fi
    echo
    print_info "后续步骤:"
    print_info "  1. 运行测试: flutter test"
    print_info "  2. 构建项目: flutter build apk"
    print_info "  3. 更新应用商店信息（如需要）"
    echo
    print_warning "注意事项:"
    print_warning "  - 如果使用了第三方服务（如Firebase），请更新相应配置"
    print_warning "  - 签名文件和密钥需要重新配置"
    print_warning "  - 已发布的应用无法通过更新推送新包名"
}

# 主函数
main() {
    # 默认参数
    NEW_PACKAGE=""
    APP_NAME=""
    CREATE_BACKUP="true"
    FORCE="false"
    VERBOSE="false"
    HAS_IOS="true"
    CURRENT_PACKAGE=""
    BACKUP_DIR=""
    
    # 解析命令行参数
    while [[ $# -gt 0 ]]; do
        case $1 in
            -n|--name)
                APP_NAME="$2"
                shift 2
                ;;
            -b|--backup)
                CREATE_BACKUP="true"
                shift
                ;;
            --no-backup)
                CREATE_BACKUP="false"
                shift
                ;;
            -f|--force)
                FORCE="true"
                shift
                ;;
            -v|--verbose)
                VERBOSE="true"
                shift
                ;;
            -h|--help)
                show_help
                exit 0
                ;;
            -*)
                print_error "未知选项: $1"
                echo "使用 -h 或 --help 查看帮助信息"
                exit 1
                ;;
            *)
                if [[ -z "$NEW_PACKAGE" ]]; then
                    NEW_PACKAGE="$1"
                else
                    print_error "多余的参数: $1"
                    exit 1
                fi
                shift
                ;;
        esac
    done
    
    # 检查必需参数
    if [[ -z "$NEW_PACKAGE" ]]; then
        print_error "请提供新的包名"
        echo "使用方法: $0 <新包名>"
        echo "使用 -h 或 --help 查看详细帮助"
        exit 1
    fi
    
    # 显示脚本信息
    echo
    print_info "=== $SCRIPT_NAME v$VERSION ==="
    echo
    
    # 验证包名
    if ! validate_package_name "$NEW_PACKAGE"; then
        exit 1
    fi
    
    # 检查环境
    check_environment
    
    # 获取当前信息
    get_current_package_name
    get_app_name
    
    # 检查是否与当前包名相同
    if [[ "$NEW_PACKAGE" == "$CURRENT_PACKAGE" ]]; then
        print_warning "新包名与当前包名相同，无需修改"
        exit 0
    fi
    
    # 显示修改信息
    echo
    print_info "修改详情:"
    print_info "  旧包名: ${CURRENT_PACKAGE:-"未知"}"
    print_info "  新包名: $NEW_PACKAGE"
    print_info "  应用名称: $APP_NAME"
    print_info "  创建备份: $([ "$CREATE_BACKUP" == "true" ] && echo "是" || echo "否")"
    echo
    
    # 确认执行
    if [[ "$FORCE" != "true" ]]; then
        print_warning "此操作将修改项目配置文件"
        read -p "确认继续? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            print_info "操作已取消"
            exit 0
        fi
    fi
    
    # 执行修改
    echo
    print_info "开始修改包名..."
    
    # 创建备份
    create_backup
    
    # 更新配置文件
    update_android_config
    update_ios_config
    
    # 清理和重新构建
    cleanup_and_rebuild
    
    # 验证结果
    if verify_changes; then
        show_completion_info
        exit 0
    else
        print_error "修改过程中发现问题"
        exit 1
    fi
}

# 运行主函数
main "$@"